<?php

	declare(ticks=1);
	
	/**
	 * @see Bike_Initial
	 */
	require_once 'Bike/Initial.php';
	

	class Bike_MultiProcess extends Bike_Initial
	{		protected $_parentPid;
		protected $_maxProcCount = 20;
		protected $_childs;
		protected $_signalQueue;
		protected $_updatePeriod = 1;  // in seconds
		protected $_childProcessFunction;

		public function init()
		{			$this->_parentPid = getmypid();
			pcntl_signal(SIGCHLD, array($this, "childSignalHandler"));
		}

		public function getParentPid()
		{			return $this->_parentPid;
		}

		public function setMaxProcCount($Count)
		{			$this->_maxProcCount = $Count;
			return $this;
		}

		public function getMaxProcCount()
		{			return $this->_maxProcCount;
		}

		protected function setFinished()
		{
			$this->_finished = True;
			return $this;
		}

		public function isFinished()
		{
			return (bool) $this->_finished;
		}

		public function setUpdatePeriod($Period)
		{
			$this->_updatePeriod = $Period;
			return $this;
		}

		public function getUpdatePeriod()
		{
			return $this->_updatePeriod;
		}

		public function setChildProcessFunction($ChildProcessFunction)
		{
			$this->_childProcessFunction = $ChildProcessFunction;
			return $this;
		}

		public function getChildProcessFunction()
		{
			return $this->_childProcessFunction;
		}

		public function getChilds($Pid=Null)
		{			if(is_null($Pid))
			{				return $this->_childs;
			}
			return $this->_childs[$Pid];
		}

		protected function resetChilds()
		{
			$this->_childs = array();
			return $this;
		}

		protected function addChild($Pid)
		{			$this->_childs[$Pid] = True;
			return $this;
		}

		protected function removeChild($Pid)
		{
			unset($this->_childs[$Pid]);
			return $this;
		}

		protected function issetChild($Pid)
		{
			return isset($this->_childs[$Pid]);
		}

		public function getSignalQueue($Pid=Null)
		{
			if(is_null($Pid))
			{
				return $this->_signalQueue;
			}
			return $this->_signalQueue[$Pid];
		}

		protected function resetSignalQueue()
		{
			$this->_signalQueue = array();
			return $this;
		}

		protected function addSignalQueue($Pid, $Status)
		{
			$this->_signalQueue[$Pid] = $Status;
			return $this;
		}

		protected function removeSignalQueue($Pid)
		{
			unset($this->_signalQueue[$Pid]);
			return $this;
		}

		protected function issetSignalQueue($Pid)
		{
			return isset($this->_signalQueue[$Pid]);
		}

		public function getCurrProcCount()
		{			return sizeof($this->getChilds());
		}

		protected function _createChildProc()
		{			while($this->getCurrProcCount() < $this->getMaxProcCount())
			{
				$pid = pcntl_fork();
			    if($pid == -1)
			    {
			    	Throw new Bike_MultiProcess_Exception('Child process creation failed');
			    }
			    elseif($pid)
			    {
			    	$this->addChild($pid);
			    	if($this->issetSignalQueue($pid))
			    	{
		                $this->childSignalHandler(SIGCHLD, $pid, $this->getSignalQueue($pid));
		            }
			    }
			    else
			    {
                    call_user_func($this->getChildProcessFunction());
			        exit;
			    }
			}
		}

		public function run()
		{			$this->resetChilds();
			$this->_createChildProc();
			while($this->getCurrProcCount())
			{				sleep($this->getUpdatePeriod());
			}
		}

		public function childSignalHandler($signo, $pid=null, $status=null)
		{
			if(!$pid)
			{
				$pid = pcntl_waitpid(-1, $status, WNOHANG);
			}

			while($pid > 0)
	        {
	            if($pid && $this->issetChild($pid))
	            {
	                $this->removeChild($pid);
	                $this->removeSignalQueue($pid);
	            }
	            else if($pid)
	            {
	                $this->addSignalQueue($pid, $status);
	            }
	            $pid = pcntl_waitpid(-1, $status, WNOHANG);
	        }
	        return True;
	    }
	}
